use super::method_descriptor::MethodDescriptor;


#[derive(Default, Debug)]
pub struct MethodDescriptorParser {
    raw: String,
    offset: usize,
    parsed: Option<MethodDescriptor>,
}

impl MethodDescriptorParser {
    pub fn parse_method_descriptor(descriptor: &str) -> MethodDescriptor {
        let mut parser = MethodDescriptorParser::default();
        parser.parse(descriptor)
    }

    pub fn parse(&mut self, descriptor: &str) -> MethodDescriptor {
        self.raw = descriptor.to_string();
        self.parsed = Some(MethodDescriptor::default());
        self.start_params();
        self.parse_param_types();
        self.end_params();
        self.parse_return_type();
        self.finish();
        self.parsed.take().unwrap()
    }

    fn start_params(&mut self) {
        if self.read_u8() != b'(' {
            self.cause_panic();
        }
    }

    fn end_params(&mut self) {
        let s = self.read_u8();
        if s != b')' {
            self.cause_panic();
        }
    }

    fn finish(&self) {
        if self.offset != self.raw.len() {
            self.cause_panic();
        }
    }

    fn cause_panic(&self) {
        panic!("BAD descriptor: {}", self.raw);
    }

    fn read_u8(&mut self) -> u8 {
        let b = self.raw.as_bytes()[self.offset];
        self.offset += 1;
        b
    }

    fn unread_u8(&mut self) {
        self.offset -= 1;
    }

    fn parse_param_types(&mut self) {
        loop {
            let t = self.parse_field_type();
            if !t.is_empty() {
                self.parsed.as_mut().unwrap().add_parameter_type(t);
            } else {
                break;
            }
        }
    }

    fn parse_return_type(&mut self) {
        if self.read_u8() == b'V' {
            self.parsed.as_mut().unwrap().set_return_type("V".to_string());
            return;
        }

        self.unread_u8();
        let t = self.parse_field_type();
        if !t.is_empty() {
            self.parsed.as_mut().unwrap().set_return_type(t);
            return;
        }
        self.cause_panic();
    }

    fn parse_field_type(&mut self) -> String {
        let s = match self.read_u8() {
            b'B' => "B",
            b'C' => "C",
            b'D' => "D",
            b'F' => "F",
            b'I' => "I",
            b'J' => "J",
            b'S' => "S",
            b'Z' => "Z",
            b'L' => self.parse_object_type(),
            b'[' => self.parse_array_type(),
            _ => {
                self.unread_u8();
                ""
            }
        };
        s.to_string()
    }

    fn parse_object_type(&mut self) -> &str {
        let unread = &self.raw[self.offset..];
        let semicolon_index = unread.find(";");
        match semicolon_index {
            None => {
                self.cause_panic();
                ""
            },
            Some(index) => {
                let obj_start = self.offset - 1;
                let obj_end = self.offset + index + 1;
                self.offset = obj_end;
                let descriptor = &self.raw[obj_start..obj_end];
                descriptor
            }
        }

    }

    fn parse_array_type(&mut self) -> &str {
        let arr_start = self.offset - 1;
        self.parse_field_type();
        let arr_end = self.offset;
        let descriptor = &self.raw[arr_start..arr_end];
        descriptor
    }


    
}